﻿// This code is released under the BSD license.
namespace PugLib.Extensions
{
    using System;
    using System.ComponentModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Reflection;

    public static class EnumExtensions
    {
        /// <summary>
        /// Checks whether a specific type is an enum type marked with a <see cref="FlagsAttribute"/>.
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms")]
        public static bool IsFlagAttribute(this Type type)
        {
            return type.IsEnum && Attribute.IsDefined(type, typeof(FlagsAttribute));
        }

        /// <summary>
        /// Checks whether a specific enum value is / contains (flags) an enum value.
        /// </summary>
        /// <param name="thisValue">The current value.</param>
        /// <param name="thatValue">The compared value.</param>
        /// <returns>
        /// 	<c>true</c> if the current value the specified is / contains the compared value(s); otherwise, <c>false</c>.
        /// </returns>
        public static bool Is(this Enum thisValue, Enum thatValue)
        {
            return Is(thisValue, thatValue, false);
        }

        /// <summary>
        /// Checks whether a specific enum value is / contains (flags) an enum value.
        /// </summary>
        /// <param name="thisValue">The current value.</param>
        /// <param name="thatValue">The compared value.</param>
        /// <param name="crossTypes">if set to <c>true</c>, different types of enumerations may be compared.</param>
        /// <returns>
        /// 	<c>true</c> if the current value the specified is / contains the compared value(s); otherwise, <c>false</c>.
        /// </returns>
        public static bool Is(this Enum thisValue, Enum thatValue, bool crossTypes)
        {
            if (!crossTypes && (thisValue.GetType() != thatValue.GetType()))
            {
                return false;
            }

            // If any of the enum values is a [Flags] enum,
            if (!IsFlagAttribute(thisValue.GetType()) && !IsFlagAttribute(thatValue.GetType()))
            {
                // Return absolute comparison.
                return thisValue.CompareTo(thatValue) == 0;
            }

            // Enum is int. HashCode of int is the int's value.
            // ((Enum)x).GetHashCode() == x
            // This is done because general Enum values (param type is "Enum") cannot be casted to integers.
            int _thisValue = thisValue.GetHashCode();
            int _thatValue = thatValue.GetHashCode();

            // Return the bitwise comparison.
            return (_thisValue & _thatValue) == _thatValue;
        }

        public static T EnumParse<T>(this string value)
        {
            return value.EnumParse<T>(false);
        }

        //  [SuppressMessage("Microsoft.Usage", "CA2208:NonConstantFieldsShouldNotBeVisible")]
        public static T EnumParse<T>(this string value, bool ignoreCase)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }
            value = value.Trim();
            if (value.Length == 0)
            {
                throw new ArgumentException("Must specify valid information for parsing in the string.", "value");
            }
            Type t = typeof(T);
            T enumType = (T)Enum.Parse(t, value, ignoreCase);
            return enumType;
        }


        public static string GetDescription(this Enum value)
        {
            FieldInfo fi = value.GetType().GetField(value.ToString());
            DescriptionAttribute[] attributes =
                (DescriptionAttribute[])fi.GetCustomAttributes(
                                             typeof(DescriptionAttribute), false);
            return (attributes.Length > 0) ? attributes[0].Description : value.ToString();
        }
    }
}